/**
 * Copyright 2018 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.modulelink.magager;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map.Entry;

import com.jianggujin.modulelink.config.JModuleConfig;
import com.jianggujin.modulelink.config.JModuleLoadPolicy;
import com.jianggujin.modulelink.exception.JModuleExistsException;
import com.jianggujin.modulelink.exception.JModuleLinkException;
import com.jianggujin.modulelink.exception.JModuleNotFoundException;
import com.jianggujin.modulelink.module.JModule;
import com.jianggujin.modulelink.util.JAssert;
import com.jianggujin.modulelink.util.JLogFactory;
import com.jianggujin.modulelink.util.JLogFactory.JLog;
import com.jianggujin.modulelink.util.JModuleUtils;

/**
 * 模块管理器默认实现
 * 
 * @author jianggujin
 *
 */
public class JDefaultModuleManager extends JAbstractModuleManager<JModule<JModuleConfig>, JModuleConfig>
        implements JModuleManager {

    private static final JLog logger = JLogFactory.getLog(JDefaultModuleManager.class);
    private volatile static JDefaultModuleManager instance = null;

    @Override
    public JModule<JModuleConfig> load(JModuleConfig moduleConfig) {
        JAssert.checkNotNull(moduleConfig, "moduleConfig must not be null");
        return this.load(moduleConfig, moduleConfig.isActive());
    }

    @Override
    public synchronized JModule<JModuleConfig> load(JModuleConfig moduleConfig, boolean active) {
        JAssert.checkNotNull(moduleConfig, "moduleConfig must not be null");
        if (logger.isInfoEnabled()) {
            logger.info("Loading module: " + moduleConfig);
        }
        String moduleName = moduleConfig.getModuleName();
        JModule<JModuleConfig> oldModule = this.modules.get(moduleName);

        // 已经存在该版本
        if (oldModule != null) {
            JModuleLoadPolicy moduleLoadPolicy = moduleConfig.getModuleLoadPolicy();
            switch (moduleLoadPolicy) {
            case AbortPolicy:
                throw new JModuleExistsException("duplicated module, module name:{}", moduleName);
            case DiscardPolicy:
                return null;
            default:
                // DO NOTHING
                break;
            }
        }
        JModule<JModuleConfig> newModule = null;
        try {
            newModule = this.getModuleLoader().load(moduleConfig, this);
            JAssert.checkNotNull(newModule, "load module is null.");
        } catch (Throwable e) {
            JModuleUtils.destroyQuietly(newModule);
            throw new JModuleLinkException("load module error.", e);
        }
        this.modules.put(moduleName, newModule);
        // 销毁原模块
        JModuleUtils.destroyQuietly(oldModule);
        if (active) {
            newModule.active();
        } else {
            newModule.unActive();
        }
        return newModule;
    }

    @Override
    public synchronized void unload(String name) {
        JAssert.checkNotNull(name, "module name must not be null");
        JModule<JModuleConfig> module = this.modules.remove(name);
        if (module == null) {
            throw new JModuleNotFoundException("could not found module with name:" + name);
        }
        JModuleUtils.destroyQuietly(module);
    }

    @Override
    public synchronized void unload() {
        Iterator<Entry<String, JModule<JModuleConfig>>> iterator = this.modules.entrySet().iterator();
        while (iterator.hasNext()) {
            Entry<String, JModule<JModuleConfig>> entry = iterator.next();
            JModuleUtils.destroyQuietly(entry.getValue());
            iterator.remove();
        }
    }

    @Override
    public JModule<JModuleConfig> optModule(String moduleName) {
        JAssert.checkNotNull(moduleName, "module name must not be null");
        return this.modules.get(moduleName);
    }

    @Override
    public Collection<JModule<JModuleConfig>> getModules() {
        return this.modules.values();
    }

    /**
     * 获得默认模块加载器
     * 
     * @return 模块加载器
     */
    public static JDefaultModuleManager getInstance() {
        if (instance == null) {
            synchronized (JDefaultModuleManager.class) {
                if (instance == null) {
                    instance = new JDefaultModuleManager();
                }
            }
        }
        return instance;
    }
}
